/*
 * Linux only has cdecl calling convention.
 * Register eax, ecx and edx are caller-saved, and the rest are callee-saved.
 */
#include <stdio.h>


int add1(int a, int b)
{
    return a + b;
}

/* equivalent to add1() */
/**
 * -O2 generate non-working code:
 * with -O2, addresses of parameters in memory depending on %ebp are incorrect, the code can be inlined.
 * with -O2, you need to record that the inline asm puts the return value inside eax (via constraints).
 */
int add2(int a, int b)
{
    __asm__ __volatile__ ("movl 12(%ebp), %eax\n\t"
                          "movl 8(%ebp), %edx\n\t"
                          "addl %edx, %eax"
    );
}

/* simplify from add2() */
/* -O2 generate non-working code. */
int add3(int a, int b)
{
    __asm__ __volatile__ ("movl 12(%ebp), %eax\n\t"
                          "addl 8(%ebp), %eax"
    );
}

/* add explicit return value from add3() */
/* -O2 generate non-working code. */
int add4(int a, int b)
{
    int res;
    /* note the double % */
    __asm__ __volatile__ ("movl 12(%%ebp), %%eax\n\t"
                          "addl 8(%%ebp), %%eax"
                          : "=a" (res)
    );
    return res;
}

/* specify to use ecx and edx from add4() */
int add5(int a, int b)
{
    int res;
    __asm__ __volatile__ ("movl %%ecx, %%eax\n\t"
                          "addl %%edx, %%eax"
                          : "=a" (res)
                          : "c" (a), "d" (b)
    );
    return res;
}

/* specify to use esi and edi from add4() */
int add6(int a, int b)
{
    int res;
    __asm__ __volatile__ ("movl %%esi, %%eax\n\t"
                          "addl %%edi, %%eax"
                          : "=a" (res)
                          : "S" (a), "D" (b)
    );
    return res;
}

/* specify to use only edx from add4() */
int add7(int a, int b)
{
    int res;
    __asm__ __volatile__ ("addl %%edx, %%eax"
                          : "=a" (res)
                          : "a" (a), "d" (b)
    );
    return res;
}

/* specify to use general registers from add4() */
int add8(int a, int b)
{
    int res;
    __asm__ __volatile__ ("movl %1, %%eax\n\t"
                          "addl %2, %%eax"
                          : "=a" (res)
                          : "r" (a), "r" (b)
    );
    return res;
}

/* specify to use general registers from add4(), add eax to clobber list. */
int add9(int a, int b)
{
    int res;
    /*
     * The clobber list tells gcc which registers(or memory) are changed by the asm,
     * but not listed as an output.
     */
    __asm__ __volatile__ ("movl %1, %0\n\t"
                          "addl %2, %0\n\t"
                          "movl %0, %%eax"
                          : "=r" (res)
                          : "r" (a), "r" (b)
                          : "%eax"
    );
    return res;
}

int main()
{
    int ret;
    ret = add1(1, 2);
    printf("ret1=%d\n", ret);
    ret = add2(3, 4);
    printf("ret2=%d\n", ret);
    ret = add3(5, 6);
    printf("ret3=%d\n", ret);
    ret = add4(7, 8);
    printf("ret4=%d\n", ret);
    ret = add5(9, 10);
    printf("ret5=%d\n", ret);
    ret = add6(11, 12);
    printf("ret6=%d\n", ret);
    ret = add7(13, 14);
    printf("ret7=%d\n", ret);
    ret = add8(15, 16);
    printf("ret8=%d\n", ret);
    ret = add8(17, 18);
    printf("ret9=%d\n", ret);
    return 0;
}

